package com.proxy;

import java.awt.Color;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;	
import javax.swing.WindowConstants;

import com.proxy.socks5.Socks5CommandRequestInboundHandler;
import com.proxy.socks5.Socks5InitialRequestInboundHandler;
import com.proxy.socks5.Socks5PasswordAuthRequestInboundHandler;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.socksx.v5.Socks5CommandRequestDecoder;
import io.netty.handler.codec.socksx.v5.Socks5InitialRequestDecoder;
import io.netty.handler.codec.socksx.v5.Socks5PasswordAuthRequestDecoder;
import io.netty.handler.codec.socksx.v5.Socks5ServerEncoder;
import io.netty.util.internal.StringUtil;

public class Socks5Proxy extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	// 得到显示器屏幕的宽高
	public int width = Toolkit.getDefaultToolkit().getScreenSize().width;
	public int height = Toolkit.getDefaultToolkit().getScreenSize().height;

	public Socks5Proxy(String port, String username, String password) {
		super();
		this.setTitle("Socks5代理");
		// 创建容器
		Container cont = getContentPane();
		// 设置为自由布局
		this.setLayout(null);

		JLabel jl1 = new JLabel("端口:");
		JTextField jtf1 = new JTextField();
		jtf1.addKeyListener(new KeyAdapter() {
			public void keyTyped(KeyEvent e) {
				int keyChar = e.getKeyChar();
				if (!(keyChar >= KeyEvent.VK_0 && keyChar <= KeyEvent.VK_9) || jtf1.getText().length() >= 5) {
					e.consume(); // 屏蔽掉非法输入
				}
			}
		});

		JLabel jl2 = new JLabel("用户:");
		JTextField jtf2 = new JTextField();

		JLabel jl3 = new JLabel("密码:");
		JTextField jtf3 = new JTextField();
		

		jtf1.setText(port);
		jtf2.setText(username);
		jtf3.setText(password);

		JLabel jl4 = new JLabel("信息:");
		JTextArea jta = new JTextArea();
		jta.setLineWrap(true); // 设置文本框为自动换行
		jta.setEditable(false); // 只读
		jta.setSelectedTextColor(Color.CYAN);
		JScrollPane jsp4 = new JScrollPane(jta); // 设置文本框有滚动条

		JButton jb1 = new JButton("启动");
		JButton jb2 = new JButton("退出");
		jb1.setCursor(new Cursor(Cursor.HAND_CURSOR));
		jb2.setCursor(new Cursor(Cursor.HAND_CURSOR));

		// 启动
		jb1.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				if ("启动".equals(jb1.getText())) {
					new Thread(()->{
						String _port = jtf1.getText();
						String _username = jtf2.getText();
						String _password = jtf3.getText();
						if(StringUtil.isNullOrEmpty(_port)) {
							_port =  "8867";
							jtf1.setText(_port);
						}
						final int port = Integer.parseInt(_port);
						final String username = _username.trim();
						final String password = _password.trim();
						String user = StringUtil.isNullOrEmpty(username) && StringUtil.isNullOrEmpty(password)
								? ""
								: username + "@" + password;
						jta.setText(user + " Run Port To: " + port);
						
						boolean isAuthentication = true;
						if(StringUtil.isNullOrEmpty(user)) {
							isAuthentication = false;
						}

						final boolean _isAuthentication = isAuthentication;
						
				        EventLoopGroup clientWorkGroup = new NioEventLoopGroup();
				        EventLoopGroup bossGroup = new NioEventLoopGroup();
				        EventLoopGroup workerGroup = new NioEventLoopGroup();
				        ServerBootstrap bootstrap = new ServerBootstrap();
				        try {
				            bootstrap.group(bossGroup, workerGroup)
				                    .channel(NioServerSocketChannel.class)
				                    .option(ChannelOption.SO_BACKLOG, 256)
				                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 1000)
				                    .childHandler(new ChannelInitializer<SocketChannel>() {
				                        @Override
				                        protected void initChannel(SocketChannel ch) throws Exception {
				                            ChannelPipeline pipeline = ch.pipeline();

				                            //socks5响应最后一个encode
				                            pipeline.addLast(Socks5ServerEncoder.DEFAULT);

				                            //处理socks5初始化请求
				                            pipeline.addLast(new Socks5InitialRequestDecoder());
				                            pipeline.addLast(new Socks5InitialRequestInboundHandler(_isAuthentication));

				                            //处理认证请求
				                            if(_isAuthentication){
				                                pipeline.addLast(new Socks5PasswordAuthRequestDecoder());
				                                pipeline.addLast(new Socks5PasswordAuthRequestInboundHandler(username, password));
				                            }
				                            //处理connection请求
				                            pipeline.addLast(new Socks5CommandRequestDecoder());
				                            Set<String> blackList = new HashSet<>();
				                            pipeline.addLast(new Socks5CommandRequestInboundHandler(clientWorkGroup, blackList));
				                        }
				                    });
				            ChannelFuture future = bootstrap.bind(port).sync();
				            System.out.println("socks5 netty server has started on port " + port);
				            future.channel().closeFuture().sync();
				        } catch (Exception ex) {
							System.out.println(ex.getMessage());
						} finally {
				            bossGroup.shutdownGracefully();
				            workerGroup.shutdownGracefully();
				        }
						
						
		            }).start();
					jtf1.setEditable(false);
					jtf2.setEditable(false);
					jtf3.setEditable(false);
					jb1.setEnabled(false);
					jb1.setText("运行中...");
					JOptionPane.showMessageDialog(null, "已开始运行");
				}
			}
		});
		// 退出
		jb2.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				// TODO Auto-generated method stub
				System.exit(0);
			}
		});

		// 控制各组件的位置及大小
		jl1.setBounds(30, 30, 120, 30);
		jtf1.setBounds(80, 30, 250, 30);

		jl2.setBounds(30, 70, 120, 30);
		jtf2.setBounds(80, 70, 250, 30);

		jl3.setBounds(30, 110, 120, 30);
		jtf3.setBounds(80, 110, 250, 30);

		jl4.setBounds(30, 150, 120, 30);
		jsp4.setBounds(80, 150, 250, 50);

		jb1.setBounds(50, 210, 100, 30);
		jb2.setBounds(210, 210, 100, 30);

		// 将空间加入到Container容器里面
		cont.add(jl1);
		cont.add(jtf1);

		cont.add(jl2);
		cont.add(jtf2);

		cont.add(jl3);
		cont.add(jtf3);

		cont.add(jb1);
		cont.add(jb2);

		cont.add(jl4);
		cont.add(jsp4);

		// 设置窗口的大小是否可以调节
		this.setResizable(false);
		// 设置窗口是否可视
		this.setVisible(true);
		// 设置窗口大小和x,y位置
		// this.setBounds(500, 260, 450, 450);
		int windowsWedth = 370, windowsHeight = 300;
		this.setBounds((width - windowsWedth) / 2, (height - windowsHeight) / 2, windowsWedth, windowsHeight);
		// 设置窗口退出则程序退出
		this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

	}

	public static void main(String[] args) {
		try {
			String _port = "8867";
            String _username = "";
            String _password = "";
            if(args != null && args.length > 0){
                if(args.length == 1){
                    _port = args[0];
                }
                if(args.length == 2){
                    _port = args[0];
                    _username = args[1];
                }
                if(args.length == 3){
                    _port = args[0];
                    _username = args[1];
                    _password = args[2];
                }
            }
            final int port = Integer.parseInt(_port);
            final String username = _username.trim();
            final String password = _password.trim();
    		new Socks5Proxy(_port, username, password);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}
	}
}
